extends "res://addons/gut/test.gd"

var FUNC := load("res://addons/rmsmartshape/plugin_functionality.gd")

signal yield_complete(number: int)


# Godot 4.0 does no longer return the function state when calling a coroutine. Hence, we need to
# build an abstraction to start multiple coroutines and then awaiting them all at once.
# See also https://github.com/godotengine/godot-proposals/issues/5673
# Implementation inspired by https://github.com/BimDav/Godot4-Coroutines
class Coroutine:
	extends RefCounted

	signal completed

	var _result: Variant = null
	var _running: bool = false

	static func run(callable: Callable) -> Coroutine:
		var coro := Coroutine.new()
		coro.start(callable)
		return coro

	func start(callable: Callable) -> void:
		_result = null
		_running = true

		@warning_ignore("redundant_await")
		_result = await callable.call()

		emit_signal("completed")
		_running = false

	func join() -> Variant:
		if _running:
			await completed
		return _result


var num_yields_complete := 0

func on_yield_complete() -> void:
	num_yields_complete += 1
	gut.p("Yields Complete %s" % num_yields_complete)
	emit_signal("yield_complete", num_yields_complete)


func after_each() -> void:
	gut.p("Teardown", 2)
	num_yields_complete = 0

# FIXME: Test not working after changes to undo-redo in Godot 4 (see delete_closed_test)
#func test_delete_point_async():
#	var shapes = []
#	var shape_count := 10
#	for _i in shape_count:
#		var shape = SS2D_Shape_Closed.new()
#		add_child_autofree(shape)
#		shapes.push_back(shape)
#
#	var coroutines: Array[Coroutine] = []
#
#	for i in shape_count:
#		var coro := Coroutine.new()
#		coro.completed.connect(on_yield_complete)
#		coro.start(delete_closed_test.bind(shapes[i], i))
#		coroutines.push_back(coro)
#
#	for coro in coroutines:
#		await coro.join()
#
#	assert_eq(num_yields_complete, shape_count)


func undo_update_method() -> void:
	pass


# FIXME: EditorUndoRedoManager is only available in EditorPlugin and as such this needs rework/removal
# func delete_closed_test(shape: SS2D_Shape_Closed, shape_idx: int = 0) -> void:
# 	var undo := UndoRedo.new()
# 	var points := get_clockwise_points()
# 	var keys := []
#
# 	for i in points.size():
# 		keys.push_back(shape.add_point(points[i]))
#
# 	# Breif await for no particular reason
# 	for _i in range(0, (randi() % 4) + 1, 1):
# 		await get_tree().physics_frame
#
# 	# Extra autogenerated point
# 	assert_eq(shape.get_point_count(), points.size() + 1, "%s: Autogen point" % shape_idx)
#
# 	# Deleted initial point and, by way of constraints, the autogenerated point
# 	# minus 2
# 	SS2D_PluginFunctionality.action_delete_point(self, "undo_update_method", undo, shape, keys[0])
# 	# New point should be added to close the shape
# 	# plus 1
# 	assert_eq(shape.get_point_count(), points.size(), "%s: New Autogen point" % shape_idx)
#
# 	# Breif await for no particular reason
# 	for _i in (randi() % 4) + 1:
# 		await get_tree().physics_frame
#
# 	# Delete point with no constraints
# 	# minus 1
# 	SS2D_PluginFunctionality.action_delete_point(self, "undo_update_method", undo, shape, keys[3])
# 	assert_eq(shape.get_point_count(), points.size() - 1, "%s: Delete Point" % shape_idx)
#
# 	undo.undo()
# 	# Plus 1
# 	assert_eq(shape.get_point_count(), points.size(), "%s: Undo Delete" % shape_idx)
#
# 	# Breif await for no particular reason
# 	for _i in (randi() % 4) + 1:
# 		await get_tree().physics_frame
#
# 	undo.undo()
# 	# Plus 1
# 	assert_eq(shape.get_point_count(), points.size() + 1, "%s: Undo Delete" % shape_idx)


func get_clockwise_points() -> Array:
	return [
		Vector2(0, 0),
		Vector2(50, -50),
		Vector2(100, 0),
		Vector2(100, 100),
		Vector2(0, 100),
		Vector2(-25, 125),
		Vector2(-50, 150),
		Vector2(-100, 100)
	]
